'''
Author: duliang thinktanker@163.com
Date: 2025-09-23 13:38:45
LastEditors: duliang thinktanker@163.com
LastEditTime: 2025-10-05 11:41:02
FilePath: 
Description: 
'''
import tkinter as tk
from tkinter import ttk
import os
import subprocess
import threading
import time
# from pathlib import Path
import ctypes
import sys
from PIL import Image, ImageTk
# import io
import win32ui
import win32api
import win32gui
# import win32process
import win32con
import psutil


class BatMonitor:
    def __init__(self, root):
        self.root = root
        self.root.title("监视器")
        self.root.geometry("800x600")  # 增加窗口宽度以容纳图片显示区域

        # 隐藏控制台窗口
        self.hide_console_window()

        # 存储监视的文件列表
        self.bat_files = [
            "大服务器", "点餐通知发送", "骆运水利网监测", "玩客云接收", "微信机器人", "文字识别", "检测玩客云文件",
            "水位记录"
        ]
        self.status_labels = {}
        self.memory_labels = {}  # 新增: 存储内存使用量标签
        self.icon_labels = {}  # 新增: 存储图标标签
        self.process_icons = {}  # 新增: 存储进程图标

        # 多线程相关
        self.lock = threading.Lock()
        self.icon_queue = {}
        self.memory_queue = {}

        # 图片显示相关
        self.image_path = r"D:\myGitee\control-net\data\onecloud"
        self.image_labels = {}  # 存储图片标签
        self.current_images = {}  # 存储当前显示的图片引用

        # 新增: 用于限制并发线程数
        self.max_threads = len(self.bat_files)
        self.active_threads = 0
        self.thread_lock = threading.Lock()

        # 新增: 用于定期清理资源
        self.cleanup_counter = 0

        # 创建界面
        self.create_widgets()

        # 启动监视线程
        self.monitoring = True
        self.monitor_thread = threading.Thread(target=self.monitor_bat_files,
                                               daemon=True)
        self.monitor_thread.start()

        # 启动UI更新线程
        self.ui_update_thread = threading.Thread(target=self.ui_update_worker,
                                                 daemon=True)
        self.ui_update_thread.start()

        # 新增: 启动图片刷新线程
        self.image_refresh_thread = threading.Thread(
            target=self.refresh_images, daemon=True)
        self.image_refresh_thread.start()

        # 新增: 添加状态栏用于显示刷新时间
        self.status_bar = tk.Frame(self.root)
        self.status_bar.pack(side=tk.BOTTOM, fill=tk.X)
        self.refresh_time_label = tk.Label(self.status_bar,
                                           text="",
                                           anchor="e")
        self.refresh_time_label.pack(side=tk.RIGHT, padx=5, pady=2)

    def hide_console_window(self):
        """隐藏控制台窗口"""
        if sys.platform.startswith('win'):
            try:
                ctypes.windll.user32.ShowWindow(
                    ctypes.windll.kernel32.GetConsoleWindow(), 0)
            except:
                pass

    def create_widgets(self):
        # 创建标题
        title_label = tk.Label(self.root, text="服务运行状态监视器", font=("Arial", 16))
        title_label.pack(pady=10)

        # 添加置顶选项
        self.always_on_top_var = tk.BooleanVar()
        self.always_on_top_check = tk.Checkbutton(
            self.root,
            text="窗口置顶",
            variable=self.always_on_top_var,
            command=self.toggle_always_on_top)
        self.always_on_top_check.pack(pady=5)

        # 创建主框架，包含状态显示和图片显示区域
        main_frame = tk.Frame(self.root)
        main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)

        # 创建文件列表框架
        self.list_frame = tk.Frame(main_frame)
        self.list_frame.pack(side=tk.LEFT,
                             fill=tk.BOTH,
                             expand=True,
                             padx=(0, 10))

        # 创建列表标题，使用grid布局确保对齐
        header_frame = tk.Frame(self.list_frame)
        header_frame.pack(fill=tk.X, pady=(0, 5))

        tk.Label(header_frame,
                 text="图标",
                 width=6,
                 anchor="w",
                 font=("Arial", 10, "bold")).grid(row=0,
                                                  column=0,
                                                  sticky="w",
                                                  padx=(0, 5))
        tk.Label(header_frame,
                 text="文件名",
                 width=20,
                 anchor="w",
                 font=("Arial", 10, "bold")).grid(row=0,
                                                  column=1,
                                                  sticky="w",
                                                  padx=(0, 5))
        tk.Label(header_frame,
                 text="状态",
                 width=15,
                 anchor="w",
                 font=("Arial", 10, "bold")).grid(row=0,
                                                  column=2,
                                                  sticky="w",
                                                  padx=(0, 5))
        tk.Label(header_frame,
                 text="内存使用",
                 width=20,
                 anchor="w",
                 font=("Arial", 10, "bold")).grid(row=0, column=3, sticky="w")

        # 创建文件状态显示区域
        self.canvas = tk.Canvas(self.list_frame)
        self.scrollbar = ttk.Scrollbar(self.list_frame,
                                       orient="vertical",
                                       command=self.canvas.yview)
        self.scrollable_frame = ttk.Frame(self.canvas)

        self.scrollable_frame.bind(
            "<Configure>", lambda e: self.canvas.configure(scrollregion=self.
                                                           canvas.bbox("all")))

        self.canvas.create_window((0, 0),
                                  window=self.scrollable_frame,
                                  anchor="nw")
        self.canvas.configure(yscrollcommand=self.scrollbar.set)

        self.canvas.pack(side="left", fill="both", expand=True)
        self.scrollbar.pack(side="right", fill="y")

        # 初始化文件状态显示
        self.init_status_display()

        # 新增: 创建图片显示区域
        self.image_frame = tk.Frame(main_frame, width=200)
        self.image_frame.pack(side=tk.RIGHT, fill=tk.Y, padx=(10, 0))
        self.image_frame.pack_propagate(False)  # 固定框架宽度

        image_title = tk.Label(self.image_frame,
                               text="玩客云截图",
                               font=("Arial", 14))
        image_title.pack(pady=5)

        # 创建图片显示的容器
        self.image_canvas = tk.Canvas(self.image_frame)
        self.image_scrollbar = ttk.Scrollbar(self.image_frame,
                                             orient="vertical",
                                             command=self.image_canvas.yview)
        self.image_scrollable_frame = ttk.Frame(self.image_canvas)

        self.image_scrollable_frame.bind(
            "<Configure>", lambda e: self.image_canvas.configure(
                scrollregion=self.image_canvas.bbox("all")))

        self.image_canvas.create_window((0, 0),
                                        window=self.image_scrollable_frame,
                                        anchor="nw")
        self.image_canvas.configure(yscrollcommand=self.image_scrollbar.set)

        self.image_canvas.pack(side="left", fill="both", expand=True)
        self.image_scrollbar.pack(side="right", fill="y")

        # 初始化图片显示区域
        self.init_image_display()

    def toggle_always_on_top(self):
        """切换窗口置顶状态"""
        self.root.attributes('-topmost', self.always_on_top_var.get())

    def init_status_display(self):
        for i, bat_file in enumerate(self.bat_files):
            frame = tk.Frame(self.scrollable_frame,
                             relief=tk.RAISED,
                             borderwidth=1)
            frame.grid_columnconfigure(0, weight=0, minsize=50)  # 图标列
            frame.grid_columnconfigure(1, weight=0, minsize=160)  # 文件名列
            frame.grid_columnconfigure(2, weight=0, minsize=120)  # 状态列
            frame.grid_columnconfigure(3, weight=1, minsize=160)  # 内存使用列
            frame.pack(fill=tk.X, pady=2)

            # 图标标签 - 增大标签尺寸以显示完整图标
            icon_label = tk.Label(frame, width=32, height=32)
            icon_label.grid(row=0, column=0, sticky="w", padx=(5, 5), pady=5)

            # 文件名标签
            name_label = tk.Label(frame, text=bat_file, width=20, anchor="w")
            name_label.grid(row=0, column=1, sticky="w", padx=(0, 5), pady=5)

            # 状态标签
            status_label = tk.Label(frame, text="未知", width=15, anchor="w")
            status_label.grid(row=0, column=2, sticky="w", padx=(0, 5), pady=5)

            # 内存使用量标签
            memory_label = tk.Label(frame, text="未知", width=20, anchor="w")
            memory_label.grid(row=0, column=3, sticky="w", padx=(0, 5), pady=5)

            self.status_labels[bat_file] = status_label
            self.memory_labels[bat_file] = memory_label
            self.icon_labels[bat_file] = icon_label

    # 新增: 初始化图片显示区域
    def init_image_display(self):
        """初始化图片显示区域"""
        if not os.path.exists(self.image_path):
            # 如果路径不存在，显示提示信息
            no_image_label = tk.Label(self.image_scrollable_frame,
                                      text=f"路径不存在:\n{self.image_path}",
                                      fg="red",
                                      justify=tk.LEFT)
            no_image_label.pack(pady=10)
            return

        image_files = [
            f for f in os.listdir(self.image_path)
            if f.lower().endswith(('.png', '.jpg', '.jpeg', '.gif', '.bmp'))
        ]

        if not image_files:
            # 如果没有图片文件，显示提示信息
            no_image_label = tk.Label(self.image_scrollable_frame,
                                      text="该目录下没有图片文件",
                                      fg="gray")
            no_image_label.pack(pady=10)
            return

        # 为每个图片文件创建显示区域
        for image_file in image_files:
            frame = tk.Frame(self.image_scrollable_frame,
                             relief=tk.RAISED,
                             borderwidth=1)
            frame.pack(fill=tk.X, pady=5, padx=5)

            # 图片文件名标签
            name_label = tk.Label(frame,
                                  text=image_file,
                                  font=("Arial", 10),
                                  anchor="w")
            name_label.pack(fill=tk.X, padx=5, pady=(5, 0))

            # 图片显示标签
            image_label = tk.Label(frame, bg="lightgray", width=32, height=32)
            image_label.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)

            # 保存标签引用
            self.image_labels[image_file] = image_label

    def is_bat_running(self, bat_filename, result_out):
        try:
            # 检查输出中是否包含目标bat文件名（在窗口标题中）
            if bat_filename.lower() in result_out:
                return True
            return False
        except Exception as e:
            print(f"检查进程时出错: {e}")
            return False

    def start_bat_file(self, bat_filename):
        try:
            if os.path.exists(bat_filename):
                # 使用os.startfile来模拟从外部打开bat文件，而不是直接运行
                os.startfile(bat_filename)
                return True
            else:
                print(f"文件不存在: {bat_filename}")
                return False
        except Exception as e:
            print(f"启动文件时出错: {e}")
            return False

    # 重写获取进程图标的方法
    def get_process_icon(self, bat_filename):
        """获取进程图标 - 重写版本"""
        try:
            # 查找与bat文件相关的窗口
            def enum_windows_callback(hwnd, results):
                if win32gui.IsWindowVisible(hwnd):
                    window_title = win32gui.GetWindowText(hwnd)
                    if bat_filename.lower() in window_title.lower():
                        results.append(hwnd)
                return True

            # 枚举所有窗口，查找包含bat文件名的窗口
            windows = []
            win32gui.EnumWindows(enum_windows_callback, windows)

            # 如果找到相关窗口，获取窗口图标
            if windows:
                hwnd = windows[0]  # 取第一个匹配的窗口
                # 尝试获取大图标
                hicon = win32gui.SendMessage(hwnd, win32con.WM_GETICON,
                                             win32con.ICON_BIG, 0)
                if not hicon:
                    # 尝试获取小图标
                    hicon = win32gui.SendMessage(hwnd, win32con.WM_GETICON,
                                                 win32con.ICON_SMALL, 0)
                if not hicon:
                    # 尝试获取窗口类图标
                    hicon = win32gui.GetClassLong(hwnd, win32con.GCL_HICON)
                if not hicon:
                    # 尝试获取窗口类小图标
                    hicon = win32gui.GetClassLong(hwnd, win32con.GCL_HICONSM)

                if hicon:
                    # 将图标转换为PIL图像
                    icon_img = self.hicon_to_pil_image(hicon)
                    if icon_img:
                        # 转换为Tkinter可用的PhotoImage，保持原始尺寸
                        tk_icon = ImageTk.PhotoImage(icon_img)
                        # 不在当前线程销毁图标，而是在主线程中处理
                        return tk_icon

            # 如果无法获取特定窗口图标，尝试通过进程获取
            for proc in psutil.process_iter(['pid', 'name', 'cmdline']):
                try:
                    # 检查进程命令行参数是否包含bat文件名
                    if proc.info['cmdline'] and any(
                            bat_filename.lower() in arg.lower()
                            for arg in proc.info['cmdline']):
                        # 获取进程可执行文件路径
                        proc_path = proc.exe()

                        # 使用ExtractIconEx获取图标
                        hicon = self.extract_icon_from_file(proc_path)
                        if hicon:
                            # 将图标转换为PIL图像
                            icon_img = self.hicon_to_pil_image(hicon)
                            if icon_img:
                                # 转换为Tkinter可用的PhotoImage，保持原始尺寸
                                tk_icon = ImageTk.PhotoImage(icon_img)
                                # 不在当前线程销毁图标，而是在主线程中处理
                                return tk_icon
                except (psutil.NoSuchProcess, psutil.AccessDenied,
                        psutil.ZombieProcess):
                    pass
                except Exception as e:
                    print(f"获取进程信息时出错: {e}")

            # 如果无法获取特定进程图标，尝试获取关联的exe文件图标
            icon = self.get_associated_exe_icon(bat_filename)
            if icon:
                return icon

            # 如果无法获取图标，返回默认图标
            return self.get_default_icon()
        except Exception as e:
            print(f"获取进程图标时出错: {e}")
            # 如果出现异常，返回默认图标
            return self.get_default_icon()

    def extract_icon_from_file(self, file_path):
        """从可执行文件中提取图标"""
        try:
            # 使用win32api.ExtractIcon获取图标
            hicon = win32gui.ExtractIcon(win32api.GetModuleHandle(None),
                                         file_path, 0)
            if hicon:
                return hicon
            return None
        except Exception as e:
            print(f"从文件提取图标时出错: {e}")
            return None

    def get_associated_exe_icon(self, bat_filename):
        """获取与bat文件关联的exe图标"""
        try:
            # 尝试获取cmd.exe的图标，因为bat文件通常由cmd.exe执行
            cmd_path = os.path.join(os.environ['SystemRoot'], 'System32',
                                    'cmd.exe')
            if os.path.exists(cmd_path):
                hicon = self.extract_icon_from_file(cmd_path)
                if hicon:
                    icon_img = self.hicon_to_pil_image(hicon)
                    if icon_img:
                        tk_icon = ImageTk.PhotoImage(icon_img)
                        # 不在当前线程销毁图标，而是在主线程中处理
                        return tk_icon
            return None
        except Exception as e:
            print(f"获取关联exe图标时出错: {e}")
            return None

    def hicon_to_pil_image(self, hicon):
        """将Windows图标句柄转换为PIL图像"""
        try:
            # 首先获取图标信息
            icon_info = win32gui.GetIconInfo(hicon)

            # 获取图标尺寸
            if hasattr(icon_info, '_iconSize'):
                icon_width, icon_height = icon_info._iconSize
            else:
                # 尝试从ICONINFO结构获取尺寸
                icon_width = win32api.GetSystemMetrics(win32con.SM_CXICON)
                icon_height = win32api.GetSystemMetrics(win32con.SM_CYICON)

            # 创建设备上下文
            hdc = win32gui.CreateCompatibleDC(0)
            # 使用win32gui.GetDC获取桌面窗口DC
            desktop_dc = win32gui.GetDC(0)
            # 创建位图
            hbmp = win32gui.CreateCompatibleBitmap(desktop_dc, icon_width,
                                                   icon_height)
            hbm_old = win32gui.SelectObject(hdc, hbmp)

            # 绘制图标到设备上下文
            win32gui.DrawIconEx(hdc, 0, 0, hicon, icon_width, icon_height, 0,
                                0, win32con.DI_NORMAL)

            # 使用win32ui获取位图数据
            try:
                bmp = win32ui.CreateBitmapFromHandle(hbmp)
                bmpinfo = bmp.GetInfo()
                bmpstr = bmp.GetBitmapBits(True)

                # 创建PIL图像
                img = Image.frombuffer(
                    'RGB', (bmpinfo['bmWidth'], bmpinfo['bmHeight']), bmpstr,
                    'raw', 'BGRX', 0, 1)
            except:
                # 备用方法：使用GetDIBits获取位图数据
                img = None

            # 清理资源
            win32gui.SelectObject(hdc, hbm_old)
            win32gui.DeleteDC(hdc)
            win32gui.ReleaseDC(0, desktop_dc)
            win32gui.DeleteObject(hbmp)

            return img
        except Exception as e:
            print(f"转换图标时出错: {e}")
            return None

    # 新增: 在主线程中销毁图标资源的方法
    def destroy_icon(self, icon):
        """在主线程中安全地销毁图标资源"""
        try:
            if icon:
                # 这里可以添加图标资源清理逻辑
                pass
        except Exception as e:
            print(f"销毁图标时出错: {e}")

    # 新增: 更新图标显示
    def update_icon(self, bat_file, icon):
        if bat_file in self.icon_labels:
            icon_label = self.icon_labels[bat_file]
            # 先销毁旧图标资源
            if hasattr(icon_label, '_icon_ref'):
                self.destroy_icon(icon_label._icon_ref)

            if icon:
                icon_label.config(image=icon)
                icon_label.image = icon  # 保持引用防止被垃圾回收
                icon_label._icon_ref = icon  # 保存图标引用用于后续清理
            else:
                icon_label.config(image='')
                icon_label._icon_ref = None

    def monitor_bat_files(self):
        while self.monitoring:
            # 修改: 同时检查cmd.exe和python.exe进程，并显示内存使用量
            result = subprocess.run(
                [
                    'tasklist',
                    '/v',
                    '/fo',
                    'csv'  # 移除过滤器以获取所有进程信息
                ],
                capture_output=True,
                text=True,
                encoding='gbk',  # Windows中文系统通常使用GBK编码
                errors='ignore')
            result_out = result.stdout.lower()

            # print("tasklist输出:", result_out)
            for bat_file in self.bat_files:
                # 检查是否在运行
                if self.is_bat_running(bat_file, result_out):
                    self.root.after(0, self.update_status, bat_file, "运行中",
                                    "green")
                    # 新增: 提取并显示内存使用量 - 放入队列由UI线程处理
                    memory_usage = self.extract_memory_usage(
                        bat_file, result.stdout)
                    self.queue_memory_update(bat_file, memory_usage)
                    # 新增: 获取并显示图标 - 放入队列由UI线程处理
                    self.queue_icon_update(bat_file)
                else:
                    self.root.after(0, self.update_status, bat_file, "已停止",
                                    "red")
                    self.root.after(0, self.update_memory, bat_file, "N/A")
                    # 修改: 使用错误图标替代None
                    error_icon = self.get_error_icon()
                    self.root.after(0, self.update_icon, bat_file, error_icon)

            # 新增: 更新状态栏刷新时间
            current_time = time.strftime("%Y-%m-%d %H:%M:%S")
            self.root.after(
                0, lambda: self.refresh_time_label.config(
                    text=f"最后刷新: {current_time}"))

            # 新增: 定期清理资源
            self.cleanup_counter += 1
            if self.cleanup_counter >= 12:  # 每分钟执行一次清理 (12 * 5秒)
                self.cleanup_resources()
                self.cleanup_counter = 0

            time.sleep(5)  # 每5秒检查一次

    # 新增多线程处理方法
    def queue_icon_update(self, bat_file):
        """将图标更新任务加入队列"""
        # 限制并发线程数
        with self.thread_lock:
            if self.active_threads >= self.max_threads:
                return
            self.active_threads += 1

        icon_thread = threading.Thread(target=self._get_and_update_icon,
                                       args=(bat_file, ),
                                       daemon=True)
        icon_thread.start()

    def _get_and_update_icon(self, bat_file):
        """在后台线程中获取图标并在主线程中更新UI"""
        try:
            icon = self.get_process_icon(bat_file)
            self.root.after(0, self.update_icon, bat_file, icon)
        finally:
            # 确保线程计数正确减少
            with self.thread_lock:
                self.active_threads -= 1

    def queue_memory_update(self, bat_file, memory_usage):
        """将内存更新任务加入队列"""
        # 限制并发线程数
        with self.thread_lock:
            if self.active_threads >= self.max_threads:
                return
            self.active_threads += 1

        memory_thread = threading.Thread(target=self._update_memory_ui,
                                         args=(bat_file, memory_usage),
                                         daemon=True)
        memory_thread.start()

    def _update_memory_ui(self, bat_file, memory_usage):
        """在主线程中更新内存显示"""
        try:
            self.root.after(0, self.update_memory, bat_file, memory_usage)
        finally:
            # 确保线程计数正确减少
            with self.thread_lock:
                self.active_threads -= 1

    # 新增: UI更新工作线程
    def ui_update_worker(self):
        """处理UI更新的后台工作线程"""
        while self.monitoring:
            time.sleep(0.1)  # 短暂休眠避免过度占用CPU

    # 新增: 提取内存使用量的方法
    def extract_memory_usage(self, bat_filename, tasklist_output):
        try:
            lines = tasklist_output.split('\n')
            for line in lines:
                if bat_filename.lower() in line.lower() and (
                        'cmd.exe' in line.lower()
                        or 'python.exe' in line.lower()):
                    # CSV格式: "映像名称","PID","会话名      ","会话#   ","内存使用"
                    parts = line.split('","')
                    if len(parts) >= 5:
                        memory = parts[4].replace('"', '').strip()
                        return memory
            return "N/A"
        except Exception as e:
            print(f"提取内存使用量时出错: {e}")
            return "N/A"

    def update_status(self, bat_file, status, color):
        if bat_file in self.status_labels:
            status_label = self.status_labels[bat_file]
            status_label.config(text=status, bg=color)

    # 新增: 更新内存使用量显示
    def update_memory(self, bat_file, memory_usage):
        if bat_file in self.memory_labels:
            memory_label = self.memory_labels[bat_file]
            memory_label.config(text=memory_usage)

    def on_closing(self):
        self.monitoring = False
        self.root.destroy()

    def get_default_icon(self):
        """获取默认图标"""
        try:
            # 获取系统默认图标（通常是应用程序图标）
            hicon = win32gui.LoadIcon(0, win32con.IDI_APPLICATION)
            if hicon:
                icon_img = self.hicon_to_pil_image(hicon)
                if icon_img:
                    tk_icon = ImageTk.PhotoImage(icon_img)
                    return tk_icon
        except Exception as e:
            print(f"获取默认图标时出错: {e}")
        return None

    def get_error_icon(self):
        """获取错误图标"""
        try:
            # 获取系统错误图标
            hicon = win32gui.LoadIcon(0, win32con.IDI_ERROR)
            if hicon:
                icon_img = self.hicon_to_pil_image(hicon)
                if icon_img:
                    tk_icon = ImageTk.PhotoImage(icon_img)
                    return tk_icon
        except Exception as e:
            print(f"获取错误图标时出错: {e}")
        return None

    # 新增: 刷新图片显示
    def refresh_images(self):
        """定时刷新图片显示"""
        while self.monitoring:
            try:
                if os.path.exists(self.image_path):
                    self.load_and_display_images()
            except Exception as e:
                print(f"刷新图片时出错: {e}")

            time.sleep(10)  # 每10秒刷新一次图片

    # 新增: 加载并显示图片
    def load_and_display_images(self):
        """加载并显示图片"""
        if not os.path.exists(self.image_path):
            return

        image_files = [
            f for f in os.listdir(self.image_path)
            if f.lower().endswith(('.png', '.jpg', '.jpeg', '.gif', '.bmp'))
        ]

        # 计算可用的显示区域大小
        max_width = max(100, self.image_frame.winfo_width() - 40)  # 减去边距和滚动条宽度
        max_height = 150  # 设置统一的最大高度

        for image_file in image_files:
            if image_file in self.image_labels:
                try:
                    image_path = os.path.join(self.image_path, image_file)
                    # 打开图片并调整大小
                    img = Image.open(image_path)

                    # 自动缩放图片以适应显示区域
                    img.thumbnail((max_width, max_height), Image.LANCZOS)
                    photo = ImageTk.PhotoImage(img)

                    # 更新图片显示
                    image_label = self.image_labels[image_file]

                    # 删除旧图片引用以释放内存
                    if hasattr(image_label, 'image'):
                        delattr(image_label, 'image')

                    image_label.config(image=photo, text='')  # 清除可能存在的错误文本
                    image_label.image = photo  # 保持引用防止被垃圾回收
                except Exception as e:
                    print(f"加载图片 {image_file} 时出错: {e}")
                    # 显示错误信息
                    image_label = self.image_labels[image_file]
                    image_label.config(image='', text='无法加载图片', fg='red')

        # 更新滚动区域，确保滚动条能正常工作
        self.image_canvas.configure(scrollregion=self.image_canvas.bbox("all"))

    # 新增: 定期清理资源
    def cleanup_resources(self):
        """定期清理资源以防止内存泄漏"""
        try:
            # 清理系统缓存
            import gc
            gc.collect()
        except Exception as e:
            print(f"清理资源时出错: {e}")


if __name__ == "__main__":
    root = tk.Tk()
    app = BatMonitor(root)
    root.protocol("WM_DELETE_WINDOW", app.on_closing)
    root.mainloop()
